#include "stm32f10x.h"
#include "stm32f10x_conf.h"
#include <string.h>
#include <stdio.h>

#include "serial.h"
#include "delay.h"

int rd_timeout = 0;
int wr_timeout = 0;

void USART_Configuration(USART_TypeDef* USARTx, int baudrate)
{
  USART_InitTypeDef USART_InitStructure;

  USART_InitStructure.USART_BaudRate = baudrate;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

  /* Configure USARTx */
  USART_Init(USARTx, &USART_InitStructure);
  
  /* Enable the USARTx */
  USART_Cmd(USARTx, ENABLE);
}

#ifdef  BUFFERED_IO

#define SEND_BUF_SIZE 300
#define RECV_BUF_SIZE 50

/* Send and receive ring buffers */
char send_buf[SEND_BUF_SIZE];// = "hello world from stm32vldiscovery!!!\r\n";
char *send_ptr = send_buf;
char *write_ptr = send_buf;
char recv_buf[RECV_BUF_SIZE];
char *recv_ptr = recv_buf;
char *read_ptr = recv_buf;

struct FIFO
{
  char ring_buf[10];
  char *write_ptr;
  char *read_ptr;
  int len;
};

char fifo_put(struct FIFO *fifo, char byte)
{
  if(fifo->read_ptr - fifo->write_ptr != 1 
     && (fifo->write_ptr - fifo->read_ptr != fifo->len - 1))
  {
    *fifo->write_ptr = byte;
    fifo->write_ptr++;
    if(fifo->write_ptr - fifo->ring_buf == fifo->len)
      fifo->write_ptr = fifo->ring_buf;
    return 1;
  }
  else
    return 0;
}

char fifo_get(struct FIFO *fifo)
{
  char byte;
  
  if(fifo->read_ptr != fifo->write_ptr)
  {
    byte = *fifo->read_ptr;
    fifo->read_ptr++;
    if(fifo->read_ptr - fifo->ring_buf == fifo->len)
      fifo->read_ptr = fifo->ring_buf;
    return byte;
  }
  else
    return EOF;
}

int fifo_empty(struct FIFO *fifo)
{
  return (fifo->read_ptr == fifo->write_ptr);
}

int fifo_full(struct FIFO *fifo)
{
  return  ((fifo->read_ptr - fifo->write_ptr == 1 
          || (fifo->write_ptr - fifo->read_ptr == fifo->len - 1)));
}

void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Enable the USART1 Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
  /* Enable the DMA1 Channel4 Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_Init(&NVIC_InitStructure);
}

void DMA_send(void *buf, uint32_t size)
{
  DMA_InitTypeDef DMA_InitStructure;

  /* Disable USART1 DMA TX Channel */
  DMA_Cmd(DMA1_Channel4, DISABLE);
  
  /* DMA1_Channel4 (triggered by USARTy Tx event) Config */
  DMA_DeInit(DMA1_Channel4);
  DMA_InitStructure.DMA_PeripheralBaseAddr = 0x40013804;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buf;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
  DMA_InitStructure.DMA_BufferSize = size;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel4, &DMA_InitStructure);
  DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);
 
  /* Enable USARTx DMA TX request */
  USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
  /* Enable USART1 DMA TX Channel */
  DMA_Cmd(DMA1_Channel4, ENABLE);
}

/* Handler of intterupts from USART1 */
void USART1_IRQHandler(void)
{
  char byte;
  
  if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
  {
    /* Read one byte from the receive data register
     * and if we have empty space place it to the buffer
     */
    byte = USART_ReceiveData(USART1);
    if(read_ptr - recv_ptr != 1 && (recv_ptr - read_ptr != RECV_BUF_SIZE - 1))
    {
      *recv_ptr = byte;
      recv_ptr++;
      if(recv_ptr - recv_buf == RECV_BUF_SIZE)
        recv_ptr = recv_buf;
    }
  }
  
  if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
  {
    /* Place one byte from send buffer to USART
     * if we have one
     */
    if(write_ptr != send_ptr)
    {
      byte = *send_ptr;
      USART_SendData(USART1, (u8)byte);
      send_ptr++;
      if(send_ptr == send_buf + SEND_BUF_SIZE)
        send_ptr = send_buf;
    }
    else
      USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
  }
}

/* Handler of intterupts from DMA channel 4 (USART1 Tx) */
void DMA1_Channel4_IRQHandler(void)
{
  if(DMA_GetITStatus(DMA1_IT_TC4) != RESET)
  {
    /* Set new block for transmission if we have it */
    if(write_ptr > send_ptr)
    {
      DMA_send(send_ptr, write_ptr - send_ptr);
      send_ptr = write_ptr;
    }
  }
  DMA_ClearITPendingBit(DMA1_IT_GL4);
}

void Serial_Initialize(int baudrate, int read_timeout, int write_timeout)
{ 
  rd_timeout = read_timeout;
  wr_timeout = write_timeout;
  
  NVIC_Configuration();

  /* DMA clock enable */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

  /* Enable the USARTx receive interrupt */
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
  
  USART_Configuration(USART1, baudrate);
  
  //DMA_send(send_buf, strlen(send_buf));
}

int ReadWillNotBlock(void)
{
  return (recv_ptr != read_ptr);
}

int WriteWillNotBlock(void)
{
  if(send_ptr != send_buf)
    return (write_ptr != send_ptr - 1);
  else
    return (write_ptr != send_buf + SEND_BUF_SIZE - 1);
}

int putchar(int ch) 
{ 
  /* Wait for empty space in transmit buffer */
  if(WaitForSingleEvent(wr_timeout, WriteWillNotBlock) != -1)
  {
    *write_ptr = ch;
    write_ptr++;
    if(write_ptr == send_buf + SEND_BUF_SIZE)
      write_ptr = send_buf;
    USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
    return ch;
  }
  else
    return EOF;
}

int getchar() 
{
  char byte = EOF;
  
  /* Wait the end of reception */
  if(WaitForSingleEvent(rd_timeout, ReadWillNotBlock) != -1)
  {
    byte = *read_ptr;
    read_ptr++;
    if(read_ptr - recv_buf == RECV_BUF_SIZE)
      read_ptr = recv_buf;
  }
  
  return byte;
}

/* Clear all buffered bytes */
void Serial_Clear()
{
  USART_ClearFlag(USART1, USART_FLAG_RXNE);
  read_ptr = recv_ptr;
}

#else

void Serial_Initialize(int baudrate, int read_timeout, int write_timeout)
{
  rd_timeout = read_timeout;
  wr_timeout = write_timeout;
  
  USART_Configuration(USART1, baudrate);
}

int ReadWillNotBlock(void)
{
  return USART_GetFlagStatus(USART1, USART_FLAG_RXNE);
}

int WriteWillNotBlock(void)
{
  return USART_GetFlagStatus(USART1, USART_FLAG_TXE);
}

int putchar(int ch) 
{ 
  /* Wait the end of transmission */
  if(WaitForSingleEvent(wr_timeout, WriteWillNotBlock) != -1)
  {
    USART_SendData(USART1, (u8) ch);
    return ch;
  }
  else
    return EOF;
}

int getchar() 
{
  char byte = EOF;
  
  /* Wait the end of reception */
  if(WaitForSingleEvent(rd_timeout, ReadWillNotBlock) != -1)
  {
    byte = USART_ReceiveData(USART1);
  }
  
  return byte;
}

/* Clear all buffered bytes */
void Serial_Clear()
{
  USART_ClearFlag(USART1, USART_FLAG_RXNE);
}

#endif

/* Put all symbols of string until terminator to stdout */
void putstring(const char *string)
{
  int i = 0;
  
  while(string[i])
  {
    putchar(string[i]);
    i++;
  }
}

/* Get string from stdin until line feed 
 * without full feeds at the start
 */
char *gets_trim(char *string)
{
  int ch = '\n';
  int i = 0;
  
  /* skip all returns and newlines */
  while (ch == '\n' || ch == '\r')
  {
    ch = getchar();
  }
  /* store all symbols until newline */
  while (ch != '\n' && ch != EOF)
  {
    string[i] = ch;
    i++;
    ch = getchar();
  }
  /* place termination zero (removing return) */
  if(i > 0)
  {
    if(string[i-1] == '\r')
      string[i-1] = 0;
    else
      string[i] = 0;
  }
  else
    string[0] = 0;
  
  return string;
}